// 函数声明式
function add (x: number, y: number) : number {
  return x + y
}
// 函数表达式
let addExpression = function (x: number, y: number) : number{
  return x + y
}
// 完整的函数类型
let myadd : (x: number, y: number) => number = function (x: number, y: number) : number {
  return x + y
} 
// 如果 你在一边指定了类型，一边没有指定类型，函数会自动推断返回值类型
let myadd2 = function(x: number, y:number) : number {
  return x + y
}
let myadd3 : (x: number, y: number) => number = function (x, y) {
  return x + y
}
// 传递给一个函数的参数必须与函数期望的得到的参数个数相同
function buildName (firstName: string, lastName: string) : string {
  return firstName + ' ' + lastName
}
buildName('Bob', 'Monkey')

// 可选参数
function buildName1 (firstName: string, lastName? : string) : string {
  return firstName + ' ' + lastName
}
buildName1('Bob')
buildName1('Bob', 'Monkey')
// 默认参数 在所有必须参数后免得带有默认值得参数都是可选的
function buildName3 (firstName: string, lastName = '123') : string {
  return firstName + ' ' + lastName
}
buildName3('Bob')
// 默认参数如果在第一位，必须传入undefined 来获取默认值
function buildName4 (firstName = 'will', lastName: string) : string {
  return firstName + ' ' + lastName
}
buildName4(undefined, 'Nicholas')
buildName4('lee', 'Nicholas')

// 剩余参数
function buildName5 (firstName: string, ...leave: string[]) : string {
  return firstName + leave.join(' ')
}
buildName5('lee', 'a', 'b', 'c', 'Nicholas')
let buildName6 : (firstName: string, ...leave: string[]) => string = buildName5


// 闭包中使用箭头函数作为匿名函数（内层参数）。可以使得内层函数的 this
// 是继承自外层函数，即例子中的箭头函数中的 this 是可以保证指向外层函数的 that的，但是外层的 that（this）却不能保证一定指向 deck
let scope_: string = 'window'
let deck = {
  suits: ["hearts", "spades", "clubs", "diamonds"],
  cards: Array(52),
  scope_: '_scope',
  createCardPicker: function() {
      // NOTE: the line below is now an arrow function, allowing us to capture 'this' right here
      console.log('this', this)
      let that = this
      return function() {
          return this.scope_
      }
  }
}

let cardPicker = deck.createCardPicker;
let pickedCard = cardPicker(); // 这时候的 this 指向了 global/window,箭头函数中的 this 是指向 window

let cardPicker1 = deck.createCardPicker() // 这时候外层函数的 this，指向了deck，即箭头函数中的 this 也是指向 deck

interface Card {
  suit: string
  card: number
}
interface Deck {
  suits: string[]
  cards: number[]
  // 定义了一个函数 createCardPicker (this: Deck) 相当于 ts 中对函数的描述，第二个冒号后面表示返回值
  // 反回了什么？ 返回了一个箭头函数
  createCardPicker(this : Deck) : () => Card
}
let deck1 : Deck = {
  suits: ["hearts", "spades", "clubs", "diamonds"],
  cards: Array(52),
  createCardPicker: function(this : Deck) { // 这里写的是类型，不是变量 deck1
      // NOTE: the line below is now an arrow function, allowing us to capture 'this' right here
      console.log('this', this)
      return () => {
        let pickedCard = Math.floor(Math.random() * 52);
        let pickedSuit = Math.floor(pickedCard / 13);

        return {suit: this.suits[pickedSuit], card: pickedCard % 13};
      }
  }
}

// 方法重载
let suits = ["hearts", "spades", "clubs", "diamonds"];

function pickCard(x: {suit: string; card: number; }[]): number;
function pickCard(x: number): {suit: string; card: number; };
function pickCard(x): any {
    // Check to see if we're working with an object/array
    // if so, they gave us the deck and we'll pick the card
    if (typeof x == "object") {
        let pickedCard = Math.floor(Math.random() * x.length);
        return pickedCard;
    }
    // Otherwise just let them pick the card
    else if (typeof x == "number") {
        let pickedSuit = Math.floor(x / 13);
        return { suit: suits[pickedSuit], card: x % 13 };
    }
}

let myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }];
let pickedCard1 = myDeck[pickCard(myDeck)];
alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);

let pickedCard2 = pickCard(15);
alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);

